package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.anno.Autowired;
import com.lagou.edu.anno.Component;
import com.lagou.edu.anno.Repository;
import com.lagou.edu.anno.Service;
import com.lagou.edu.cache.*;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.*;

/**
 * @author 应癫
 *
 * 工厂类，生产对象（使用反射技术）
 */
public class AnnoBeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String,Object> map = new HashMap<>();  // 存储对象

    static {
        // 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
        // 加载xml
        InputStream resourceAsStream = AnnoBeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        // 解析xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            // 获取包路径
            List<Element> beanList = rootElement.selectNodes("//anno");
            Set<String> pathSet = new HashSet<>();
            for (int i = 0; i < beanList.size(); i++) {
                Element element =  beanList.get(i);
                // 处理每个bean元素，获取到该元素的id 和 class 属性
                String basePath = element.attributeValue("basePath");        // accountDao
                pathSet.add(basePath);
            }
            // 扫描包路径下的Class类，并暂存在三级缓存
            for(String path: pathSet) {

                String filePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + path.replace(".", "\\");
                filePath = URLDecoder.decode(filePath, "UTF-8");
                scanClass(path, filePath);
            }

            // 为引用属性赋值，并提升缓存级别
            setField();

            // 检验二级缓存中的对象是否都已初始化完成，如果完成则存入一级缓存

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 扫描注解类
    public static void scanClass(String path, String filePath) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        File pFile = new File(filePath);
        if(pFile.isDirectory()) {
            File[] files = pFile.listFiles();
            for(File f: files) {
                if(f.isDirectory()) {
                    scanClass(path + "." + f.getName(), f.getAbsolutePath());
                } else {
                    scanClass(path, f.getAbsolutePath());
                }

            }
        } else {
            String fName = pFile.getName();
            Class<?> aClass = Class.forName(path + "." + fName.substring(0, fName.indexOf(".")));
            String classNameDef = "";
            if(!aClass.isAnnotation() && !aClass.isInterface()) {
                if(aClass.getAnnotations().length == 0) {
                    return;
                }
                for(Annotation a: aClass.getAnnotations()) {
                    if(a instanceof Service) {
                        classNameDef = ((Service) a).value();
                        break;
                    } else if(a instanceof Repository) {
                        classNameDef = ((Repository) a).value();
                        break;
                    } else if(a instanceof Component) {
                        classNameDef = ((Component) a).value();
                        break;
                    }
                }

                Object o = aClass.newInstance();

                ThirdLevelCache.set(StringUtils.isEmpty(classNameDef)? aClass.getName(): classNameDef, o);

                // 遍历属性应用
                setReferCache(o);

            }
        }
    }


    public static void setReferCache(Object c) {
        Field[] declaredFields = c.getClass().getDeclaredFields();
        for(Field field: declaredFields) {
            field.setAccessible(true);
            Annotation annotation = field.getAnnotation(Autowired.class);
            if(null != annotation) {
                String fieldName = field.getName();
                String className = field.getType().getName();
                ReferNameCache.set(fieldName, c);
                ReferClassCache.set(className, c);
            }
        }
    }

    public static void setField() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Set<String> keys = ThirdLevelCache.getKeySet();
        Iterator<String> keyIt = keys.iterator();
        while(keyIt.hasNext()) {
            String key = keyIt.next();
            List<Object> classes = ReferNameCache.get(key);

            if(null == classes) {
                classes = ReferClassCache.get(key);
            }

            // 如果没有被引用，则直接进入一级缓存
            if(null == classes || classes.size() == 0) {
                FirstLevelCache.set(key, ThirdLevelCache.get(key));
                keyIt.remove();
                continue;
            }

            // 如果被引用，则进行属性赋值，并放入二级缓存
            for (Object parent: classes) {
                for(Field field : parent.getClass().getDeclaredFields()) {
                    field.setAccessible(true);
                    if(field.getName().equals(key) || field.getGenericType().getTypeName().toString().equals(key)) {
                        field.set(parent, ThirdLevelCache.get(key));
                    }
                }

            }
//            直接放入一级缓存，暂不考虑循环依赖
            FirstLevelCache.set(key, ThirdLevelCache.get(key));
            keyIt.remove();
        }


    }

    public static void fieldCheck() {
        // TODO 更新校验检查逻辑

    }


    public static Object getBean(String id) {
        return FirstLevelCache.get(id);
    }

    public static void main(String[] args) {
        System.out.println(AnnoBeanFactory.getBean("com.lagou.edu.dao.impl.JdbcAccountDaoImpl"));
    }
}
